﻿using AngleSharp.Dom;
using Masuit.Tools;
using Masuit.Tools.Strings;
using Microsoft.AspNetCore.Mvc.Routing;
using NewLife;

namespace SimpleX.RBAC
{
    public class SysMenuService : DbRepository<SysMenu>, ISysMenuService
    {
        private readonly ISysUserService _sysUserService;
        private readonly ICacheService _cacheService;

        public SysMenuService(ISysUserService sysUserService,
            ICacheService cacheService)
        {
            _sysUserService = sysUserService;
            _cacheService = cacheService;
        }

        #region 获取菜单

        public async Task<List<SysMenu>> GetOwnMenus()
        {
            var result = new List<SysMenu>();
            //读取缓存信息
            result = _cacheService.HashGetOne<List<SysMenu>>(CacheConst.Cache_UserRelation, CacheConst.Field_UserHasMenu(UserManager.UserId));
            if (result != null && result.Count > 0)
            {
                return result;
            }

            //获取用户信息
            var userInfo = await _sysUserService.GetSysUserByAccount(UserManager.UserAccount);
            if (userInfo != null)
            {
                //获取所有的菜单和模块以及单页面列表，并按分类和排序码排序
                var categorys = new string[] { CateGoryConst.Menu_MODULE, CateGoryConst.Menu_MENU, CateGoryConst.Menu_SPA };
                var allModuleAndMenuAndSpaList = await GetListAsync(x => categorys.Contains(x.Category));

                //定义菜单ID列表
                HashSet<string> menuIdList = new HashSet<string>();

                if (UserManager.SuperAdmin)
                {
                    menuIdList.AddRange(allModuleAndMenuAndSpaList.Where(x => x.Category == CateGoryConst.Menu_MENU).Select(x => x.Id).ToList());
                }
                else
                {
                    var menuIds = await GetUserMenus(UserManager.UserId);
                    menuIdList.AddRange(menuIds);
                }

                //以下代码全部copy自国东兄

                allModuleAndMenuAndSpaList = allModuleAndMenuAndSpaList.OrderBy(it => it.Category).ThenBy(it => it.SortCode).ToList();

                List<SysMenu> allModuleList = new List<SysMenu>();//模块列表
                List<SysMenu> allMenuList = new List<SysMenu>();//菜单列表
                List<SysMenu> allSpaList = new List<SysMenu>();//单页列表
                                                               //遍历菜单集合
                allModuleAndMenuAndSpaList.ForEach(it =>
                {
                    switch (it.Category)
                    {
                        case CateGoryConst.Menu_MODULE://模块
                            it.Name = it.Title;//设置Name等于title
                            allModuleList.Add(it);//添加到模块列表
                            break;

                        case CateGoryConst.Menu_MENU://菜单
                            allMenuList.Add(it);//添加到菜单列表
                            break;

                        case CateGoryConst.Menu_SPA://单页
                            allSpaList.Add(it);//添加到单页列表
                            break;
                    }
                });
                //获取我的菜单列表
                var myMenus = allMenuList.Where(it => menuIdList.Contains(it.Id)).ToList();
                // 对获取到的角色对应的菜单列表进行处理，获取父列表
                var parentList = GetMyParentMenus(allMenuList, myMenus);
                myMenus.AddRange(parentList);//合并列表
                                             //获取我的模块信息Id列表
                var moduleIds = myMenus.Select(it => it.Module).Distinct().ToList();
                //获取我的模块集合
                var myModules = GetMyModules(allModuleList, moduleIds, allSpaList.Count);
                myMenus.AddRange(myModules);//模块添加到菜单列表
                // 遍历单页列表
                allSpaList.ForEach(it =>
                {
                    // 将第一个模块作为所有单页面的所属模块，并添加
                    var firstModuleId = myModules[0].Id;
                    it.ParentId = firstModuleId;
                    it.Module = firstModuleId;
                });

                myMenus.AddRange(allSpaList);//但也添加到菜单

                //构建meta
                ConstructMeta(myMenus, allSpaList[0].Id);
                //构建菜单树
                result = ConstructMenuTrees(myMenus);
            }

            if (result.Count > 0)
                _cacheService.HashAdd(CacheConst.Cache_UserRelation, CacheConst.Field_UserHasMenu(UserManager.UserId), result);

            return result;
        }

        private List<SysMenu> GetMyModules(List<SysMenu> allModuleList, List<string> moduleIds, int spaCount)
        {
            //获取我的模块信息
            var myModules = allModuleList.Where(it => moduleIds.Contains(it.Id)).ToList();
            // 如果一个模块都没拥有
            if (myModules.Count == 0)
            {
                // 如果系统中无模块（极端情况）
                if (allModuleList.Count == 0)
                {
                    if (spaCount == 0)// 如果系统中无单页面，则返回空列表
                    {
                        return new List<SysMenu>();
                    }
                    else
                    {
                        // 否则构造一个模块，并添加到拥有模块
                        SysMenu sysResource = new SysMenu();
                        sysResource.Id = IDUtils.GetId();
                        sysResource.Path = "/" + new NumberFormater(91).ToString(new Random().Next(100000, int.MaxValue));
                        sysResource.Category = CateGoryConst.Menu_MODULE;
                        allModuleList.Add(sysResource);
                        myModules.Add(sysResource);
                    }
                }
                else
                {
                    // 否则将系统中第一个模块作为拥有的模块
                    myModules.Add(allModuleList[0]);
                }
            }
            return myModules;
        }

        private IEnumerable<SysMenu> GetMyParentMenus(List<SysMenu> allMenuList, List<SysMenu> myMenus)
        {
            var parentList = new List<SysMenu>();
            myMenus.ForEach(it =>
            {
                //找到父ID对应的菜单
                var parent = allMenuList.Where(r => r.Id == it.ParentId).FirstOrDefault();
                if (parent != null && !parentList.Contains(parent) && !myMenus.Contains(parent))//如果不为空且两个列表里没有
                {
                    parentList.Add(parent);//添加到父列表
                }
            });
            return parentList; ;
        }

        public List<SysMenu> ConstructMenuTrees(List<SysMenu> resourceList, string parentId = "0")
        {
            //找下级资源ID列表
            var resources = resourceList.Where(it => it.ParentId == parentId).OrderBy(it => it.SortCode).ToList();
            if (resources.Count > 0)//如果数量大于0
            {
                var data = new List<SysMenu>();
                foreach (var item in resources)//遍历资源
                {
                    item.Children = ConstructMenuTrees(resourceList, item.Id);//添加子节点
                    data.Add(item);//添加到列表
                }
                return data;//返回结果
            }
            return new List<SysMenu>();
        }

        private void ConstructMeta(List<SysMenu> myMenus, string firstSpaId)
        {
            myMenus.ForEach(it =>
            {
                // 将模块的父id设置为0，设置随机path
                if (it.Category == CateGoryConst.Menu_MODULE)
                {
                    it.ParentId = "0";
                    it.Path = "/p" + IDUtils.GetId();
                }
                // 将根菜单的父id设置为模块的id
                if (it.Category == CateGoryConst.Menu_MENU)
                {
                    if (it.ParentId == "0")
                    {
                        it.ParentId = it.Module;
                    }
                }
                //定义meta
                Meta meta = new Meta { Icon = it.Icon, Title = it.Title, Type = it.Category.ToLower() };
                // 如果是菜单，则设置type菜单类型为小写
                if (it.Category == CateGoryConst.Menu_MENU)
                {
                    if (it.MenuType != CateGoryConst.Menu_CATALOG)//菜单类型不是目录
                    {
                        meta.Type = it.MenuType.ToLower();
                    }
                }
                // 如果是单页面
                if (it.Category == CateGoryConst.Menu_SPA)
                {
                    meta.Type = CateGoryConst.Menu_MENU.ToLower();//类型等于菜单
                    if (it.Id == firstSpaId)
                    {
                        // 如果是首页（第一个单页面）则设置affix
                        meta.Affix = true;
                    }
                    else
                    {
                        // 否则隐藏该单页面
                        meta.hidden = true;
                    }
                }
                it.Meta = meta;
            });
        }

        #endregion

        #region 关系

        private async Task<List<string>> GetUserMenus(string userId)
        {
            //用户有多少菜单
            var userHasMenuIds = await Context.Queryable<SysRelation>().Where(x => x.Category == CateGoryConst.Relation_SYS_USER_HAS_MENU && x.ObjectId == userId)
                                                                .Select(x => x.TargetId).ToListAsync();

            //用户有多少角色
            var userHasRoleIds = await Context.Queryable<SysRelation>().Where(x => x.Category == CateGoryConst.Relation_SYS_USER_HAS_ROLE && x.ObjectId == userId)
                                                                .Select(x => x.TargetId).ToListAsync();

            //角色有多少菜单
            var roleHasMenuIds = await Context.Queryable<SysRelation>().Where(x => x.Category == CateGoryConst.Relation_SYS_ROLE_HAS_MENU && userHasRoleIds.Contains(x.ObjectId))
                                                               .Select(x => x.TargetId).ToListAsync();

            userHasMenuIds.AddRange(roleHasMenuIds);

            return userHasMenuIds.Distinct().ToList();
        }

        #endregion

        public async Task Add(MenuAddInput input)
        {
            var result = await CheckInput(input);//检查参数
            if (result != "OK")
            {
                Unify.SetError(result);
                return;
            }
            var sysResource = input.Adapt<SysMenu>();//实体转换

            await InsertAsync(sysResource);
        }

        private async Task<string> CheckInput(SysMenu sysMenu)
        {
            //获取所有菜单列表
            var menList = await GetListAsync(x => x.Category == CateGoryConst.Menu_MENU);
            //判断是否有同级且同名的菜单
            if (menList.Any(it => it.ParentId == sysMenu.ParentId && it.Title == sysMenu.Title && it.Id != sysMenu.Id))
                return ($"存在重复的菜单名称:{sysMenu.Title}");
            if (sysMenu.ParentId != SimpleXConst.Zero)
            {
                //获取父级,判断父级ID正不正确
                var parent = menList.Where(it => it.Id == sysMenu.ParentId).FirstOrDefault();
                if (parent != null)
                {
                    if (parent.Module != sysMenu.Module)//如果父级的模块和当前模块不一样
                        return ($"模块与上级菜单不一致");
                    if (parent.Id == sysMenu.Id)
                        return ($"上级菜单不能选择自己");
                }
                else
                {
                    return ($"上级菜单不存在:{sysMenu.ParentId}");
                }
            }
            return "OK";
        }

        public async Task<List<SysMenu>> Tree(MenuTreeInput input)
        {
            //获取所有菜单
            var sysMenus = await GetListAsync(x => x.Category == CateGoryConst.Menu_MENU);
            sysMenus = sysMenus.WhereIF(!string.IsNullOrEmpty(input.Module), it => it.Module == input.Module)//根据模块查找
                .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Title == input.SearchKey)//根据关键字查找
                .ToList();
            //构建菜单树
            var tree = ConstructMenuTrees(sysMenus);
            return tree;
        }

        public async Task Edit(MenuEditInput input)
        {
            var result = await CheckInput(input);//检查参数
            if (result != "OK")
            {
                Unify.SetError(result);
                return;
            }
            //删除用户的接口权限缓存
            _cacheService.Remove(CacheConst.Cache_UserRelation);

            var sysResource = input.Adapt<SysMenu>();//实体转换
            await UpdateAsync(sysResource);
        }

        public async Task Delete(List<BaseIdInput> input)
        {
            var ids = input.Select(it => it.Id).ToList();
            if (ids.Count > 0)
            {
                //获取所有菜单和按钮
                var resourceList = await GetListAsync(x => x.Category == CateGoryConst.Menu_MENU || x.Category == CateGoryConst.Menu_BUTTON);

                //找到要删除的菜单
                var sysResources = resourceList.Where(it => ids.Contains(it.Id)).ToList();
                //查找内置菜单
                var system = sysResources.Where(it => it.Code == "system").FirstOrDefault();
                if (system != null)
                {
                    Unify.SetError($"不可删除系统菜单:{system.Title}");
                    return;
                }
                //删除用户的接口权限缓存
                _cacheService.Remove(CacheConst.Cache_UserRelation);

                //需要删除的资源ID列表
                var resourceIds = new List<string>();
                //遍历菜单列表
                sysResources.ForEach(it =>
                {
                    //获取菜单所有子节点
                    var child = GetChildListById(resourceList, it.Id, false);
                    //将子节点ID添加到删除资源ID列表
                    resourceIds.AddRange(child.Select(it => it.Id).ToList());
                    resourceIds.Add(it.Id);//添加到删除资源ID列表
                });
                ids.AddRange(resourceIds);//添加到删除ID列表
                                          //事务
                var result = await itenant.UseTranAsync(async () =>
                {
                    await DeleteByIdsAsync(ids.Cast<object>().ToArray());//删除菜单和按钮
                    await Context.Deleteable<SysRelation>()//关系表删除对应SYS_ROLE_HAS_RESOURCE
                     .Where(it => it.Category == CateGoryConst.Relation_SYS_ROLE_HAS_MENU && resourceIds.Contains(it.TargetId)).ExecuteCommandAsync();
                });
                if (result.IsSuccess)//如果成功了
                {
                }
                else
                {
                    //写日志
                    throw result.ErrorException;
                }
            }
        }

        public List<SysMenu> GetChildListById(List<SysMenu> sysResources, string resId, bool isContainOneself = true)
        {
            //查找下级
            var childLsit = GetResourceChilden(sysResources, resId);
            if (isContainOneself)//如果包含自己
            {
                //获取自己的机构信息
                var self = sysResources.Where(it => it.Id == resId).FirstOrDefault();
                if (self != null) childLsit.Insert(0, self);//如果机构不为空就插到第一个
            }
            return childLsit;
        }

        public List<SysMenu> GetResourceChilden(List<SysMenu> resourceList, string parentId)
        {
            //找下级资源ID列表
            var resources = resourceList.Where(it => it.ParentId == parentId).ToList();
            if (resources.Count > 0)//如果数量大于0
            {
                var data = new List<SysMenu>();
                foreach (var item in resources)//遍历资源
                {
                    var res = GetResourceChilden(resourceList, item.Id);
                    data.AddRange(res);//添加子节点;
                    data.Add(item);//添加到列表
                }
                return data;//返回结果
            }
            return new List<SysMenu>();
        }

        public async Task ChangeModule(MenuChangeModuleInput input)
        {
            var sysResource = await GetByIdAsync(input.Id);
            if (sysResource != null)
            {
                if (sysResource.Module == input.Module)//如果模块ID没变直接返回
                    return;
                if (sysResource.ParentId != SimpleXConst.Zero)
                {
                    Unify.SetError($"非顶级菜单不可修改所属模块");
                    return;
                }
                //获取所有菜单和模块
                var resourceList = await GetListAsync(x => x.Category == CateGoryConst.Menu_MENU || x.Category == CateGoryConst.Menu_MODULE);

                if (!resourceList.Any(it => it.Category == CateGoryConst.Menu_MODULE && it.Id == input.Module))
                {
                    Unify.SetError($"不存在的模块");
                    return;
                }
                //获取所有菜单
                var menuList = resourceList.Where(it => it.Category == CateGoryConst.Menu_MENU).ToList();
                //获取需要改变模块菜单的所有子菜单
                var childList = GetChildListById(menuList, input.Id);
                childList.ForEach(it => it.Module = input.Module);
                //更新数据
                await UpdateRangeAsync(childList);
            }
        }

        public async Task<List<SysMenu>> GetListByCategory(string category)
        {
            return await GetListAsync(it => it.Category == category);
        }

        #region 反射菜单api接口

        public List<MenuOutput> GetAuthCodeAttributes(MenuAuthInput input)
        {
            var retval = new List<MenuOutput>();

            // 获取所有的需要授权的控制器
            var controllerTypes = App.EffectiveTypes.Where(type => typeof(ControllerBase).IsAssignableFrom(type) && type.IsPublic && !type.IsAbstract);
            var authorizedControllerTypes = controllerTypes.Where(type => type.GetCustomAttributes(typeof(AuthorizeAttribute), true).Length > 0);

            foreach (var authorized in authorizedControllerTypes)
            {
                var routeAttributes = authorized.GetCustomAttributes<RouteAttribute>().ToList();
                if (!routeAttributes.Any())
                    continue;

                var route = routeAttributes[0];//取第一个值
                var routeName = GetRouteName(authorized.Name, route.Template);//赋值路由名称
                if (input.RouteName.EqualIgnoreCase(routeName))
                {
                    var menthods = authorized.GetMethods().Where(type => type.GetCustomAttributes(typeof(ActionPermissionAttribute), true).Length > 0);

                    foreach (var menthod in menthods)
                    {
                        var noAction = menthod.GetCustomAttribute<NonActionAttribute>();
                        if (noAction != null)
                            continue;

                        var authCode = menthod.GetCustomAttribute<ActionPermissionAttribute>();

                        var outPut = new MenuOutput
                        {
                            //RouteName = routeName,
                            MethodName = authCode.Name,
                            AuthType = authCode.AuthType,
                        };

                        var httpMethod = menthod.GetCustomAttribute<HttpMethodAttribute>();
                        var methodPath = httpMethod.Template.ToLower();

                        if (routeName.EndsWith("/"))
                            outPut.MethodPath = routeName + methodPath;
                        else
                            outPut.MethodPath = routeName + "/" + methodPath;

                        outPut.MethodCode = routeName.TrimStart("/").Replace("/", ":");
                        if (!outPut.MethodCode.EndsWith(":"))
                            outPut.MethodCode += ":";

                        outPut.MethodCode += methodPath;
                        outPut.MethodCode = outPut.MethodCode.ToLower();

                        retval.Add(outPut);
                    }
                }
            }

            return retval;
        }

        private string GetRouteName(string controllerName, string template)
        {
            if (!template.StartsWith("/"))
                template = "/" + template;//如果路由名称不是/开头则加上/防止控制器没写
            if (template.Contains("[controller]"))
            {
                controllerName = controllerName.Replace("Controller", "");//去掉Controller
                template = template.Replace("[controller]", controllerName);//替换[controller]
            }
            return template.ToLower();
        }

        #endregion

        #region 角色菜单树 ResourceTreeSelector

        public async Task<List<MenuTreeSelector>> ResourceTreeSelector()
        {
            List<MenuTreeSelector> resourceTreeSelectors = new List<MenuTreeSelector>();//定义结果
                                                                                        //获取模块列表
            var moduleList = await GetListByCategory(CateGoryConst.Menu_MODULE);
            //遍历模块
            foreach (var module in moduleList)
            {
                //将实体转换为ResourceTreeSelectorOutPut获取基本信息
                var resourceTreeSelector = module.Adapt<MenuTreeSelector>();
                resourceTreeSelector.Menu = await GetRoleGrantResourceMenus(module.Id);
                resourceTreeSelectors.Add(resourceTreeSelector);
            }
            return resourceTreeSelectors;
        }

        public async Task<List<MenuTreeSelector.RoleGrantResourceMenu>> GetRoleGrantResourceMenus(string moduleId)
        {
            var roleGrantResourceMenus = new List<MenuTreeSelector.RoleGrantResourceMenu>();//定义结果
            List<SysMenu> allMenuList = (await GetListByCategory(CateGoryConst.Menu_MENU)).Where(it => it.Module == moduleId).ToList();//获取所有菜单列表
            List<SysMenu> allButtonList = await GetListByCategory(CateGoryConst.Menu_BUTTON);//获取所有按钮列表
            var parentMenuList = allMenuList.Where(it => it.ParentId == SimpleXConst.Zero).ToList();//获取一级目录

            //遍历一级目录
            foreach (var parent in parentMenuList)
            {
                //如果是目录则去遍历下级
                if (parent.MenuType == CateGoryConst.Menu_CATALOG)
                {
                    //获取所有下级菜单
                    var menuList = GetChildListById(allMenuList, parent.Id, false);
                    if (menuList.Count > 0)//如果有菜单
                    {
                        //遍历下级菜单
                        foreach (var menu in menuList)
                        {
                            //如果菜单类型是菜单
                            if (menu.MenuType == CateGoryConst.Menu_MENU)
                            {
                                //获取菜单下按钮集合并转换成对应实体
                                var buttonList = allButtonList.Where(it => it.ParentId == menu.Id).ToList();
                                var buttons = buttonList.Adapt<List<MenuTreeSelector.RoleGrantResourceButton>>();
                                roleGrantResourceMenus.Add(new MenuTreeSelector.RoleGrantResourceMenu
                                {
                                    Id = menu.Id,
                                    ParentId = parent.Id,
                                    ParentName = parent.Title,
                                    Module = moduleId,
                                    Title = GetRoleGrantResourceMenuTitle(menuList, menu),//菜单名称需要特殊处理因为有二级菜单
                                    Button = buttons
                                });
                            }
                            else if (menu.MenuType == CateGoryConst.LINK || menu.MenuType == CateGoryConst.IFRAME)//如果是内链或者外链
                            {
                                //直接加到资源列表
                                roleGrantResourceMenus.Add(new MenuTreeSelector.RoleGrantResourceMenu
                                {
                                    Id = menu.Id,
                                    ParentId = parent.Id,
                                    ParentName = parent.Title,
                                    Module = moduleId,
                                    Title = menu.Title,
                                });
                            }
                        }
                    }
                    else
                    {
                        //否则就将自己加到一级目录里面
                        roleGrantResourceMenus.Add(new MenuTreeSelector.RoleGrantResourceMenu
                        {
                            Id = parent.Id,
                            ParentId = parent.Id,
                            ParentName = parent.Title,
                            Module = moduleId
                        });
                    }
                }
                else
                {
                    //就将自己加到一级目录里面
                    var roleGrantResourcesButtons = new MenuTreeSelector.RoleGrantResourceMenu
                    {
                        Id = parent.Id,
                        ParentId = parent.Id,
                        ParentName = parent.Title,
                        Module = moduleId,
                        Title = parent.Title,
                    };
                    //如果菜单类型是菜单
                    if (parent.MenuType == CateGoryConst.Menu_MENU)
                    {
                        //获取菜单下按钮集合并转换成对应实体
                        var buttonList = allButtonList.Where(it => it.ParentId == parent.Id).ToList();
                        roleGrantResourcesButtons.Button = buttonList.Adapt<List<MenuTreeSelector.RoleGrantResourceButton>>();
                    }
                    roleGrantResourceMenus.Add(roleGrantResourcesButtons);
                }
            }
            return roleGrantResourceMenus;
        }

        private string GetRoleGrantResourceMenuTitle(List<SysMenu> menuList, SysMenu menu)
        {
            //查找菜单上级
            var parentList = GetResourceParent(menuList, menu.ParentId);
            //如果有父级菜单
            if (parentList.Count > 0)
            {
                var titles = parentList.Select(it => it.Title).ToList();//提取出父级的name
                var title = string.Join("- ", titles) + $"-{menu.Title}";//根据-分割,转换成字符串并在最后加上菜单的title
                return title;
            }
            else
            {
                return menu.Title;//原路返回
            }
        }

        public List<SysMenu> GetResourceParent(List<SysMenu> resourceList, string parentId)
        {
            //找上级资源ID列表
            var resources = resourceList.Where(it => it.Id == parentId).FirstOrDefault();
            if (resources != null)//如果数量大于0
            {
                var data = new List<SysMenu>();
                var parents = GetResourceParent(resourceList, resources.ParentId);
                data.AddRange(parents);//添加子节点;
                data.Add(resources);//添加到列表
                return data;//返回结果
            }
            return new List<SysMenu>();
        }

        #endregion
    }
}